/************************************************************************/
/*			KEGS: Apple //gs Emulator			*/
/*			Copyright 2002 by Kent Dickey			*/
/*									*/
/*		This code is covered by the GNU GPL			*/
/*									*/
/*	The KEGS web page is kegs.sourceforge.net			*/
/*	You may contact the author at: kadickey@alumni.princeton.edu	*/
/************************************************************************/

const char rcsid_iwm_c[] = "@(#)$KmKId: iwm.c,v 1.111 2003-11-03 22:14:10-05 kentd Exp $";

#include "defc.h"

extern int Verbose;
extern int g_vbl_count;
extern int speed_fast;
extern word32 g_slot_motor_detect;

const byte phys_to_dos_sec[] = {
	0x00, 0x07, 0x0e, 0x06,  0x0d, 0x05, 0x0c, 0x04,
	0x0b, 0x03, 0x0a, 0x02,  0x09, 0x01, 0x08, 0x0f
};

const byte phys_to_prodos_sec[] = {
	0x00, 0x08, 0x01, 0x09,  0x02, 0x0a, 0x03, 0x0b,
	0x04, 0x0c, 0x05, 0x0d,  0x06, 0x0e, 0x07, 0x0f
};


const byte to_disk_byte[] = {
	0x96, 0x97, 0x9a, 0x9b,  0x9d, 0x9e, 0x9f, 0xa6,
	0xa7, 0xab, 0xac, 0xad,  0xae, 0xaf, 0xb2, 0xb3,
/* 0x10 */
	0xb4, 0xb5, 0xb6, 0xb7,  0xb9, 0xba, 0xbb, 0xbc,
	0xbd, 0xbe, 0xbf, 0xcb,  0xcd, 0xce, 0xcf, 0xd3,
/* 0x20 */
	0xd6, 0xd7, 0xd9, 0xda,  0xdb, 0xdc, 0xdd, 0xde,
	0xdf, 0xe5, 0xe6, 0xe7,  0xe9, 0xea, 0xeb, 0xec,
/* 0x30 */
	0xed, 0xee, 0xef, 0xf2,  0xf3, 0xf4, 0xf5, 0xf6,
	0xf7, 0xf9, 0xfa, 0xfb,  0xfc, 0xfd, 0xfe, 0xff
};

int	g_track_bytes_35[] = {
	0x200*12,
	0x200*11,
	0x200*10,
	0x200*9,
	0x200*8
};

int	g_track_nibs_35[] = {
	816*12,
	816*11,
	816*10,
	816*9,
	816*8
};



int	g_fast_disk_emul = 1;
int	g_slow_525_emul_wr = 0;
double	g_dcycs_end_emul_wr = 0.0;
int	g_fast_disk_unnib = 0;
int	g_iwm_fake_fast = 0;


int	from_disk_byte[256];
int	from_disk_byte_valid = 0;

Iwm	iwm;

int	g_apple35_sel = 0;
int	head_35 = 0;
int	g_iwm_motor_on = 0;

int	g_check_nibblization = 0;

/* prototypes for IWM special routs */
int iwm_read_data_35(Disk *dsk, int fast_disk_emul, double dcycs);
int iwm_read_data_525(Disk *dsk, int fast_disk_emul, double dcycs);
void iwm_write_data_35(Disk *dsk, word32 val, int fast_disk_emul, double dcycs);
void iwm_write_data_525(Disk *dsk, word32 val, int fast_disk_emul,double dcycs);

void
iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525)
{
	int	num_tracks;
	int	i;

	num_tracks = MAX_TRACKS;

	dsk->dcycs_last_read = 0.0;
	dsk->name_ptr = 0;
	dsk->partition_name = 0;
	dsk->partition_num = -1;
	dsk->fd = -1;
	dsk->force_size = 0;
	dsk->image_start = 0;
	dsk->image_size = 0;
	dsk->smartport = smartport;
	dsk->disk_525 = disk_525;
	dsk->drive = drive;
	dsk->cur_qtr_track = 0;
	dsk->image_type = 0;
	dsk->vol_num = 254;
	dsk->write_prot = 1;
	dsk->write_through_to_unix = 0;
	dsk->disk_dirty = 0;
	dsk->just_ejected = 0;
	dsk->last_phase = 0;
	dsk->nib_pos = 0;
	dsk->num_tracks = 0;

	for(i = 0; i < num_tracks; i++) {
		dsk->tracks[i].dsk = dsk;
		dsk->tracks[i].nib_area = 0;
		dsk->tracks[i].track_dirty = 0;
		dsk->tracks[i].overflow_size = 0;
		dsk->tracks[i].track_len = 0;
		dsk->tracks[i].unix_pos = -1;
		dsk->tracks[i].unix_len = -1;
	}
}

void
iwm_init()
{
	int	val;
	int	i;

	for(i = 0; i < 2; i++) {
		iwm_init_drive(&(iwm.drive525[i]), 0, i, 1);
		iwm_init_drive(&(iwm.drive35[i]), 0, i, 0);
	}

	for(i = 0; i < MAX_C7_DISKS; i++) {
		iwm_init_drive(&(iwm.smartport[i]), 1, i, 0);
	}

	if(from_disk_byte_valid == 0) {
		for(i = 0; i < 256; i++) {
			from_disk_byte[i] = -1;
		}
		for(i = 0; i < 64; i++) {
			val = to_disk_byte[i];
			from_disk_byte[val] = i;
		}
		from_disk_byte_valid = 1;
	} else {
		halt_printf("iwm_init called twice!\n");
	}

	iwm_reset();
}

void
iwm_reset()
{
	iwm.q6 = 0;
	iwm.q7 = 0;
	iwm.motor_on = 0;
	iwm.motor_on35 = 0;
	iwm.motor_off = 0;
	iwm.motor_off_vbl_count = 0;
	iwm.step_direction35 = 0;
	iwm.head35 = 0;
	iwm.drive_select = 0;
	iwm.iwm_mode = 0;
	iwm.enable2 = 0;
	iwm.reset = 0;
	iwm.iwm_phase[0] = 0;
	iwm.iwm_phase[1] = 0;
	iwm.iwm_phase[2] = 0;
	iwm.iwm_phase[3] = 0;
	iwm.previous_write_val = 0;
	iwm.previous_write_bits = 0;

	g_iwm_motor_on = 0;
	g_apple35_sel = 0;
}

void
draw_iwm_status(int line, char *buf)
{
	char	*flag[2][2];

	flag[0][0] = " ";
	flag[0][1] = " ";
	flag[1][0] = " ";
	flag[1][1] = " ";

	if(g_iwm_motor_on) {
		flag[g_apple35_sel][iwm.drive_select] = "*";
	}

	sprintf(buf, "s6d1:%2d%s   s6d2:%2d%s   s5d1:%2d/%d%s   "
		"s5d2:%2d/%d%s fast_disk_emul:%d,%d c036:%02x",
		iwm.drive525[0].cur_qtr_track >> 2, flag[0][0],
		iwm.drive525[1].cur_qtr_track >> 2, flag[0][1],
		iwm.drive35[0].cur_qtr_track >> 1,
		iwm.drive35[0].cur_qtr_track & 1, flag[1][0],
		iwm.drive35[1].cur_qtr_track >> 1,
		iwm.drive35[1].cur_qtr_track & 1, flag[1][1],
		g_fast_disk_emul, g_slow_525_emul_wr,
		(speed_fast << 7) + g_slot_motor_detect);

	video_update_status_line(line, buf);
}

void
iwm_flush_disk_to_unix(Disk *dsk)
{
	byte	buffer[0x4000];
	int	num_dirty;
	int	j;
	int	ret;
	int	unix_pos;
	int	unix_len;

	if(dsk->disk_dirty == 0 || dsk->write_through_to_unix == 0) {
		return;
	}

	printf("Writing disk %s to Unix\n", dsk->name_ptr);
	dsk->disk_dirty = 0;
	num_dirty = 0;

	/* Dirty data! */
	for(j = 0; j < dsk->num_tracks; j++) {

		ret = disk_track_to_unix(dsk, j, &(buffer[0]));

		if(ret != 1 && ret != 0) {
			printf("iwm_flush_disk_to_unix ret: %d, cannot write "
				"image to unix\n", ret);
			halt_printf("Adjusting image not to write through!\n");
			dsk->write_through_to_unix = 0;
			break;
		}

		if(ret != 1) {
			/* not at an even track, or not dirty */
			continue;
		}
		if((j & 3) != 0 && dsk->disk_525) {
			halt_printf("Valid data on a non-whole trk: %03x\n", j);
			continue;
		}

		num_dirty++;

		/* Write it out */
		unix_pos = dsk->tracks[j].unix_pos;
		unix_len = dsk->tracks[j].unix_len;
		if(unix_pos < 0 || unix_len < 0x1000) {
			halt_printf("Disk:%s trk:%d, unix_pos:%08x, len:%08x\n",
				dsk->name_ptr, j, unix_pos, unix_len);
			break;
		}

		ret = lseek(dsk->fd, unix_pos, SEEK_SET);
		if(ret != unix_pos) {
			halt_printf("lseek 525: %08x, errno: %d\n", ret, errno);
		}

		ret = write(dsk->fd, &(buffer[0]), unix_len);
		if(ret != unix_len) {
			printf("write: %08x, errno:%d, qtrk: %02x, disk: %s\n",
				ret, errno, j, dsk->name_ptr);
		}
	}

	if(num_dirty == 0) {
		halt_printf("Drive %s was dirty, but no track was dirty!\n",
			dsk->name_ptr);
	}

}

/* Check for dirty disk 3 times a second */

void
iwm_vbl_update(int doit_3_persec)
{
	Disk	*dsk;
	int	motor_on;
	int	i;

	if(iwm.motor_on && iwm.motor_off) {
		if(iwm.motor_off_vbl_count <= g_vbl_count) {
			printf("Disk timer expired, drive off: %08x\n",
				g_vbl_count);
			iwm.motor_on = 0;
			iwm.motor_off = 0;
		}
	}

	if(!doit_3_persec) {
		return;
	}

	motor_on = iwm.motor_on;
	if(g_apple35_sel) {
		motor_on = iwm.motor_on35;
	}

	if(motor_on == 0 || iwm.motor_off) {
		/* Disk not spinning, see if any dirty tracks to flush */
		/*  out to Unix */
		for(i = 0; i < 2; i++) {
			dsk = &(iwm.drive525[i]);
			iwm_flush_disk_to_unix(dsk);
		}
		for(i = 0; i < 2; i++) {
			dsk = &(iwm.drive35[i]);
			iwm_flush_disk_to_unix(dsk);
		}
	}
}


void
iwm_show_stats()
{
	printf("IWM stats: q7,q6: %d, %d, reset,enable2: %d,%d, mode: %02x\n",
		iwm.q7, iwm.q6, iwm.reset, iwm.enable2, iwm.iwm_mode);
	printf("motor: %d,%d, motor35:%d drive: %d, on: %d, head35: %d "
		"phs: %d %d %d %d\n",
		iwm.motor_on, iwm.motor_off, g_iwm_motor_on,
		iwm.drive_select, g_apple35_sel,
		head_35, iwm.iwm_phase[0], iwm.iwm_phase[1], iwm.iwm_phase[2],
		iwm.iwm_phase[3]);
	printf("iwm.drive525[0].fd: %d, [1].fd: %d\n",
		iwm.drive525[0].fd, iwm.drive525[1].fd);
	printf("iwm.drive525[0].last_phase: %d, [1].last_phase: %d\n",
		iwm.drive525[0].last_phase, iwm.drive525[1].last_phase);
}

void
iwm_touch_switches(int loc, double dcycs)
{
	Disk	*dsk;
	int	phase;
	int	on;
	int	drive;

	if(iwm.reset) {
		iwm_printf("IWM under reset: %d, enable2: %d\n", iwm.reset,
			iwm.enable2);
	}

	on = loc & 1;
	drive = iwm.drive_select;
	phase = loc >> 1;
	if(g_apple35_sel) {
		dsk = &(iwm.drive35[drive]);
	} else {
		dsk = &(iwm.drive525[drive]);
	}


	if(loc < 8) {
		/* phase adjustments.  See if motor is on */

		iwm.iwm_phase[phase] = on;

		if(iwm.motor_on) {
			if(g_apple35_sel) {
				if(phase == 3 && on) {
					iwm_do_action35(dcycs);
				}
			} else if(on) {
				/* Move apple525 head */
				iwm525_phase_change(drive, phase);
			}
		}
		/* See if enable or reset is asserted */
		if(iwm.iwm_phase[0] && iwm.iwm_phase[2]) {
			iwm.reset = 1;
			iwm_printf("IWM reset active\n");
		} else {
			iwm.reset = 0;
		}
		if(iwm.iwm_phase[1] && iwm.iwm_phase[3]) {
			iwm.enable2 = 1;
			iwm_printf("IWM ENABLE2 active\n");
		} else {
			iwm.enable2 = 0;
		}
	} else {
		/* loc >= 8 */
		switch(loc) {
		case 0x8:
			iwm_printf("Turning IWM motor off!\n");
			if(iwm.iwm_mode & 0x04) {
				/* Turn off immediately */
				iwm.motor_off = 0;
				iwm.motor_on = 0;
			} else {
				/* 1 second delay */
				if(iwm.motor_on && !iwm.motor_off) {
					iwm.motor_off = 1;
					iwm.motor_off_vbl_count = g_vbl_count
									+ 60;
				}
			}

			if(g_iwm_motor_on || g_slow_525_emul_wr) {
				/* recalc current speed */
				set_halt(HALT_EVENT);
			}

			g_iwm_motor_on = 0;
			g_slow_525_emul_wr = 0;
			break;
		case 0x9:
			iwm_printf("Turning IWM motor on!\n");
			iwm.motor_on = 1;
			iwm.motor_off = 0;

			if(g_iwm_motor_on == 0) {
				/* recalc current speed */
				set_halt(HALT_EVENT);
			}
			g_iwm_motor_on = 1;

			break;
		case 0xa:
		case 0xb:
			iwm.drive_select = on;
			break;
		case 0xc:
		case 0xd:
			iwm.q6 = on;
			break;
		case 0xe:
		case 0xf:
			iwm.q7 = on;
			break;
		default:
			printf("iwm_touch_switches: loc: %02x unknown!\n", loc);
			exit(2);
		}
	}

	if(!iwm.q7) {
		iwm.previous_write_bits = 0;
	}

	if((dcycs > g_dcycs_end_emul_wr) && g_slow_525_emul_wr) {
		set_halt(HALT_EVENT);
		g_slow_525_emul_wr = 0;
	}
}

void
iwm_move_to_track(Disk *dsk, int new_track)
{
	int	disk_525;
	int	dr;

	disk_525 = dsk->disk_525;

	if(new_track < 0) {
		new_track = 0;
	}
	if(new_track >= dsk->num_tracks) {
		if(disk_525) {
			new_track = dsk->num_tracks - 4;
		} else {
			new_track = dsk->num_tracks - 2 + iwm.head35;
		}

		if(new_track <= 0) {
			new_track = 0;
		}
	}

	if(dsk->cur_qtr_track != new_track) {
		dr = dsk->drive + 1;
		if(disk_525) {
			iwm_printf("s6d%d Track: %d.%02d\n", dr,
				new_track >> 2, 25* (new_track & 3));
		} else {
			iwm_printf("s5d%d Track: %d Side: %d\n", dr,
				new_track >> 1, new_track & 1);
		}

		dsk->cur_qtr_track = new_track;
	}
}

void
iwm525_phase_change(int drive, int phase)
{
	Disk	*dsk;
	int	qtr_track;
	int	last_phase;
	int	phase_up;
	int	phase_down;
	int	delta;

	phase_up = (phase - 1) & 3;
	phase_down = (phase + 1) & 3;

	dsk = &(iwm.drive525[drive]);
	last_phase = dsk->last_phase;

	qtr_track = dsk->cur_qtr_track;

	delta = 0;
	if(last_phase == phase_up) {
		delta = 2;
		last_phase = phase;
	} else if(last_phase == phase_down) {
		delta = -2;
		last_phase = phase;
	}

	qtr_track += delta;
	if(qtr_track < 0) {
		printf("GRIND...GRIND...GRIND\n");
		qtr_track = 0;
		last_phase = 0;
	}
	if(qtr_track > 4*34) {
		printf("Disk arm moved past track 0x21, moving it back\n");
		qtr_track = 4*34;
		last_phase = 0;
	}

	iwm_move_to_track(dsk, qtr_track);

	dsk->last_phase = last_phase;

	iwm_printf("Moving drive to qtr track: %04x, %d, %d, %d,   "
		"%d %d %d %d\n",
		qtr_track, phase, delta, last_phase, iwm.iwm_phase[0],
		iwm.iwm_phase[1], iwm.iwm_phase[2], iwm.iwm_phase[3]);

	/* sanity check stepping algorithm */
	if((qtr_track & 7) == 0) {
		/* check for just access phase 0 */
		if(last_phase != 0 ) {
			halt_printf("last_phase: %d!\n", last_phase);
		}
	}
}

int
iwm_read_status35(double dcycs)
{
	Disk	*dsk;
	int	drive;
	int	state;
	int	tmp;

	drive = iwm.drive_select;
	dsk = &(iwm.drive35[drive]);

	if(iwm.motor_on) {
		/* Read status */
		state = (iwm.iwm_phase[1] << 3) + (iwm.iwm_phase[0] << 2) +
			(head_35 << 1) + iwm.iwm_phase[2];

		iwm_printf("Iwm status read state: %02x\n", state);

		switch(state) {
		case 0x00:	/* step direction */
			return iwm.step_direction35;
			break;
		case 0x01:	/* lower head activate */
			/* also return instantaneous data from head */
			iwm.head35 = 0;
			iwm_move_to_track(dsk, (dsk->cur_qtr_track & (-2)));
			return (((int)dcycs) & 1);
			break;
		case 0x02:	/* disk in place */
			/* 1 = no disk, 0 = disk */
			iwm_printf("read disk in place, num_tracks: %d\n",
				dsk->num_tracks);
			tmp = (dsk->num_tracks <= 0);
			return tmp;
			break;
		case 0x03:	/* upper head activate */
			/* also return instantaneous data from head */
			iwm.head35 = 1;
			iwm_move_to_track(dsk, (dsk->cur_qtr_track | 1));
			return (((int)dcycs) & 1);
			break;
		case 0x04:	/* disk is stepping? */
			/* 1 = not stepping, 0 = stepping */
			return 1;
			break;
		case 0x05:	/* Unknown function of ROM 03? */
			/* 1 = or $20 into 0xe1/f24+drive, 0 = don't */
			return 1;
			break;
		case 0x06:	/* disk is locked */
			/* 0 = locked, 1 = unlocked */
			return (!dsk->write_prot);
			break;
		case 0x08:	/* motor on */
			/* 0 = on, 1 = off */
			return !iwm.motor_on35;
			break;
		case 0x09:	/* number of sides */
			/* 1 = 2 sides, 0 = 1 side */
			return 1;
			break;
		case 0x0a:	/* at track 0 */
			/* 1 = not at track 0, 0 = there */
			tmp = (dsk->cur_qtr_track != 0);
			iwm_printf("Read at track0_35: %d\n", tmp);
			return tmp;
			break;
		case 0x0b:	/* disk ready??? */
			/* 0 = ready, 1 = not ready? */
			tmp = !iwm.motor_on35;
			iwm_printf("Read disk ready, ret: %d\n", tmp);
			return tmp;
			break;
		case 0x0c:	/* disk switched?? */
			/* 0 = not switched, 1 = switched? */
			tmp = (dsk->just_ejected != 0);
			iwm_printf("Read disk switched: %d\n", tmp);
			return tmp;
			break;
		case 0x0d:	/* false read when ejecting disk */
			return 1;
		case 0x0e:	/* tachometer */
			halt_printf("Reading tachometer!\n");
			return (((int)dcycs) & 1);
			break;
		case 0x0f:	/* drive installed? */
			/* 0 = drive exists, 1 = no drive */
			if(drive) {
				/* pretend no drive 1 */
				return 1;
			}
			return 0;
			break;
		default:
			halt_printf("Read 3.5 status, state: %02x\n", state);
			return 1;
		}
	} else {
		iwm_printf("Read 3.5 status with drive off!\n");
		return 1;
	}
}

void
iwm_do_action35(double dcycs)
{
	Disk	*dsk;
	int	drive;
	int	state;

	drive = iwm.drive_select;
	dsk = &(iwm.drive35[drive]);

	if(iwm.motor_on) {
		/* Perform action */
		state = (iwm.iwm_phase[1] << 3) + (iwm.iwm_phase[0] << 2) +
			(head_35 << 1) + iwm.iwm_phase[2];
		switch(state) {
		case 0x00:	/* Set step direction inward */
			/* towards higher tracks */
			iwm.step_direction35 = 0;
			iwm_printf("Iwm set step dir35 = 0\n");
			break;
		case 0x01:	/* Set step direction outward */
			/* towards lower tracks */
			iwm.step_direction35 = 1;
			iwm_printf("Iwm set step dir35 = 1\n");
			break;
		case 0x03:	/* reset disk-switched flag? */
			iwm_printf("Iwm reset disk switch\n");
			dsk->just_ejected = 0;
			/* set_halt(1); */
			break;
		case 0x04:	/* step disk */
			if(iwm.step_direction35) {
				iwm_move_to_track(dsk, dsk->cur_qtr_track - 2);
			} else {
				iwm_move_to_track(dsk, dsk->cur_qtr_track + 2);
			}
			break;
		case 0x08:	/* turn motor on */
			iwm_printf("Iwm set motor_on35 = 1\n");
			iwm.motor_on35 = 1;
			break;
		case 0x09:	/* turn motor off */
			iwm_printf("Iwm set motor_on35 = 0\n");
			iwm.motor_on35 = 0;
			break;
		case 0x0d:	/* eject disk */
			eject_disk(dsk);
			break;
		case 0x02:
		case 0x07:
		case 0x0b: /* hacks to allow AE 1.6MB driver to not crash me */
			break;
		default:
			halt_printf("Do 3.5 action, state: %02x\n", state);
			return;
		}
	} else {
		halt_printf("Set 3.5 status with drive off!\n");
		return;
	}
}

void
iwm_set_apple35_sel(int newval)
{
	if(g_apple35_sel != newval) {
		/* Handle speed changes */
		set_halt(HALT_EVENT);
	}

	g_apple35_sel = newval;
}

int
iwm_read_c0ec(double dcycs)
{
	Disk	*dsk;
	int	drive;

	iwm.q6 = 0;

	if(iwm.q7 == 0 && iwm.enable2 == 0 && iwm.motor_on) {
		drive = iwm.drive_select;
		if(g_apple35_sel) {
			dsk = &(iwm.drive35[drive]);
			return iwm_read_data_35(dsk, g_fast_disk_emul, dcycs);
		} else {
			dsk = &(iwm.drive525[drive]);
			return iwm_read_data_525(dsk, g_fast_disk_emul, dcycs);
		}

	}

	return read_iwm(0xc, dcycs);
}


int
read_iwm(int loc, double dcycs)
{
	Disk	*dsk;
	word32	status;
	double	diff_dcycs;
	double	dcmp;
	int	on;
	int	state;
	int	drive;
	int	val;

	loc = loc & 0xf;
	on = loc & 1;

	if(loc == 0xc) {
		iwm.q6 = 0;
	} else {
		iwm_touch_switches(loc, dcycs);
	}

	state = (iwm.q7 << 1) + iwm.q6;
	drive = iwm.drive_select;
	if(g_apple35_sel) {
		dsk = &(iwm.drive35[drive]);
	} else {
		dsk = &(iwm.drive525[drive]);
	}

	if(on) {
		/* odd address, return 0 */
		return 0;
	} else {
		/* even address */
		switch(state) {
		case 0x00:	/* q7 = 0, q6 = 0 */
			if(iwm.enable2) {
				return iwm_read_enable2(dcycs);
			} else {
				if(iwm.motor_on) {
					return iwm_read_data(dsk,
						g_fast_disk_emul, dcycs);
				} else {
					iwm_printf("read iwm st 0, m off!\n");
/* HACK!!!! */
					return 0xff;
					//return (((int)dcycs) & 0x7f) + 0x80;
				}
			}
			break;
		case 0x01:	/* q7 = 0, q6 = 1 */
			/* read IWM status reg */
			if(iwm.enable2) {
				iwm_printf("Read status under enable2: 1\n");
				status = 1;
			} else {
				if(g_apple35_sel) {
					status = iwm_read_status35(dcycs);
				} else {
					status = dsk->write_prot;
				}
			}

			val = (status << 7) + (iwm.motor_on << 5) +
				iwm.iwm_mode;
			iwm_printf("Read status: %02x\n", val);

			return val;
			break;
		case 0x02:	/* q7 = 1, q6 = 0 */
			/* read handshake register */
			if(iwm.enable2) {
				return iwm_read_enable2_handshake(dcycs);
			} else {
				status = 0xc0;
				diff_dcycs = dcycs - dsk->dcycs_last_read;
				dcmp = 16.0;
				if(dsk->disk_525 == 0) {
					dcmp = 32.0;
				}
				if(diff_dcycs > dcmp) {
					iwm_printf("Write underrun!\n");
					iwm_printf("cur: %f, dc_last: %f\n",
						dcycs, dsk->dcycs_last_read);
					status = status & 0xbf;
				}
				return status;
			}
			break;
		case 0x03:	/* q7 = 1, q6 = 1 */
			halt_printf("read iwm state 3!\n");
			return 0;
		break;
		}
		
	}
	halt_printf("Got to end of read_iwm, loc: %02x!\n", loc);

	return 0;
}

void
write_iwm(int loc, int val, double dcycs)
{
	Disk	*dsk;
	int	on;
	int	state;
	int	drive;
	int	fast_writes;

	loc = loc & 0xf;
	on = loc & 1;

	iwm_touch_switches(loc, dcycs);

	state = (iwm.q7 << 1) + iwm.q6;
	drive = iwm.drive_select;
	fast_writes = g_fast_disk_emul;
	if(g_apple35_sel) {
		dsk = &(iwm.drive35[drive]);
	} else {
		dsk = &(iwm.drive525[drive]);
		fast_writes = !g_slow_525_emul_wr && fast_writes;
	}

	if(on) {
		/* odd address, write something */
		if(state == 0x03) {
			/* q7, q6 = 1,1 */
			if(iwm.motor_on) {
				if(iwm.enable2) {
					iwm_write_enable2(val, dcycs);
				} else {
					iwm_write_data(dsk, val,
						fast_writes, dcycs);
				}
			} else {
				/* write mode register */
				val = val & 0x1f;
				iwm.iwm_mode = val;
				if(val != 0 && val != 0x0f && val != 0x07 &&
						val != 0x04 && val != 0x0b) {
					halt_printf("set iwm_mode:%02x!\n",val);
				}
			}
		} else {
			if(iwm.enable2) {
				iwm_write_enable2(val, dcycs);
			} else {
				printf("Write iwm1, st: %02x, loc: %x: %02x\n",
					state, loc, val);
			}
		}
		return;
	} else {
		/* even address */
		if(iwm.enable2) {
			iwm_write_enable2(val, dcycs);
		} else {
			iwm_printf("Write iwm2, st: %02x, loc: %x: %02x\n",
				state, loc, val);
		}
		return;
	}

	halt_printf("Got to end of write_iwm, loc:%02x, val: %02x\n", loc, val);

	return;

}



int
iwm_read_enable2(double dcycs)
{
	iwm_printf("Read under enable2!\n");
	return 0xff;
}

int g_cnt_enable2_handshake = 0;

int
iwm_read_enable2_handshake(double dcycs)
{
	int	val;

	iwm_printf("Read handshake under enable2!\n");

	val = 0xc0;
	g_cnt_enable2_handshake++;
	if(g_cnt_enable2_handshake > 3) {
		g_cnt_enable2_handshake = 0;
		val = 0x80;
	}

	return val;
}

void
iwm_write_enable2(int val, double dcycs)
{
	iwm_printf("Write under enable2: %02x!\n", val);

	return;
}

int
iwm_read_data(Disk *dsk, int fast_disk_emul, double dcycs)
{
	if(dsk->disk_525) {
		return iwm_read_data_525(dsk, fast_disk_emul, dcycs);
	} else {
		return iwm_read_data_35(dsk, fast_disk_emul, dcycs);
	}
}

void
iwm_write_data(Disk *dsk, word32 val, int fast_disk_emul, double dcycs)
{
	if(dsk->disk_525) {
		iwm_write_data_525(dsk, val, fast_disk_emul, dcycs);
	} else {
		iwm_write_data_35(dsk, val, fast_disk_emul, dcycs);
	}
}

#undef IWM_READ_ROUT
#undef IWM_WRITE_ROUT
#undef IWM_CYC_MULT
#undef IWM_DISK_525

#define IWM_READ_ROUT		iwm_read_data_35
#define IWM_WRITE_ROUT		iwm_write_data_35
#define IWM_CYC_MULT		1
#define IWM_DISK_525		0

#define INCLUDE_IWM_RCSID_C
#include "iwm_35_525.h"
#undef INCLUDE_IWM_RCSID_C

#undef IWM_READ_ROUT
#undef IWM_WRITE_ROUT
#undef IWM_CYC_MULT
#undef IWM_DISK_525

#define IWM_READ_ROUT		iwm_read_data_525
#define IWM_WRITE_ROUT		iwm_write_data_525
#define IWM_CYC_MULT		2
#define IWM_DISK_525		1
#include "iwm_35_525.h"

#undef IWM_READ_ROUT
#undef IWM_WRITE_ROUT
#undef IWM_CYC_MULT
#undef IWM_DISK_525





/* c600 */
void
sector_to_partial_nib(byte *in, byte *nib_ptr)
{
	byte	*aux_buf;
	byte	*nib_out;
	int	val;
	int	val2;
	int	x;
	int	i;

	/* Convert 256(+1) data bytes to 342+1 disk nibbles */

	aux_buf = nib_ptr;
	nib_out = nib_ptr + 0x56;

	for(i = 0; i < 0x56; i++) {
		aux_buf[i] = 0;
	}

	x = 0x55;
	for(i = 0x101; i >= 0; i--) {
		val = in[i];
		if(i >= 0x100) {
			val = 0;
		}
		val2 = (aux_buf[x] << 1) + (val & 1);
		val = val >> 1;
		val2 = (val2 << 1) + (val & 1);
		val = val >> 1;
		nib_out[i] = val;
		aux_buf[x] = val2;
		x--;
		if(x < 0) {
			x = 0x55;
		}
	}
}


int
disk_unnib_4x4(Disk *dsk)
{
	int	val1;
	int	val2;

	val1 = iwm_read_data(dsk, 1, 0);
	val2 = iwm_read_data(dsk, 1, 0);

	return ((val1 << 1) + 1) & val2;
}

int
iwm_denib_track525(Disk *dsk, Track *trk, int qtr_track, byte *outbuf)
{
	byte	aux_buf[0x80];
	byte	*buf;
	int	sector_done[16];
	int	num_sectors_done;
	int	track_len;
	int	vol, track, phys_sec, log_sec, cksum;
	int	val;
	int	val2;
	int	prev_val;
	int	x;
	int	my_nib_cnt;
	int	save_qtr_track;
	int	save_nib_pos;
	int	tmp_nib_pos;
	int	status;
	int	i;

	save_qtr_track = dsk->cur_qtr_track;
	save_nib_pos = dsk->nib_pos;

	iwm_move_to_track(dsk, qtr_track);

	dsk->nib_pos = 0;
	g_fast_disk_unnib = 1;

	track_len = trk->track_len;

	for(i = 0; i < 16; i++) {
		sector_done[i] = 0;
	}

	num_sectors_done = 0;

	val = 0;
	status = -1;
	my_nib_cnt = 0;
	while(my_nib_cnt++ < 2*track_len) {
		/* look for start of a sector */
		if(val != 0xd5) {
			val = iwm_read_data(dsk, 1, 0);
			continue;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0xaa) {
			continue;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0x96) {
			continue;
		}

		/* It's a sector start */
		vol = disk_unnib_4x4(dsk);
		track = disk_unnib_4x4(dsk);
		phys_sec = disk_unnib_4x4(dsk);
		if(phys_sec < 0 || phys_sec > 15) {
			printf("Track %02x, read sec as %02x\n", qtr_track>>2,
				phys_sec);
			break;
		}
		if(dsk->image_type == DSK_TYPE_DOS33) {
			log_sec = phys_to_dos_sec[phys_sec];
		} else {
			log_sec = phys_to_prodos_sec[phys_sec];
		}
		cksum = disk_unnib_4x4(dsk);
		if((vol ^ track ^ phys_sec ^ cksum) != 0) {
			/* not correct format */
			printf("Track %02x not DOS 3.3 since hdr cksum, %02x "
				"%02x %02x %02x\n",
				qtr_track>>2, vol, track, phys_sec, cksum);
			break;
		}

		/* see what sector it is */
		if(track != (qtr_track>>2) || (phys_sec < 0)||(phys_sec > 15)) {
			printf("Track %02x bad since track: %02x, sec: %02x\n",
				qtr_track>>2, track, phys_sec);
			break;
		}

		if(sector_done[phys_sec]) {
			printf("Already done sector %02x on track %02x!\n",
				phys_sec, qtr_track>>2);
			break;
		}

		/* So far so good, let's do it! */
		val = 0;
		i = 0;
		while(i < NIBS_FROM_ADDR_TO_DATA) {
			i++;
			if(val != 0xd5) {
				val = iwm_read_data(dsk, 1, 0);
				continue;
			}

			val = iwm_read_data(dsk, 1, 0);
			if(val != 0xaa) {
				continue;
			}

			val = iwm_read_data(dsk, 1, 0);
			if(val != 0xad) {
				continue;
			}

			/* got it, just break */
			break;
		}

		if(i >= NIBS_FROM_ADDR_TO_DATA) {
			printf("No data header, track %02x, sec %02x\n",
				qtr_track>>2, phys_sec);
			printf("nib_pos: %08x\n", dsk->nib_pos);
			break;
		}

		buf = outbuf + 0x100*log_sec;

		/* Data start! */
		prev_val = 0;
		for(i = 0x55; i >= 0; i--) {
			val = iwm_read_data(dsk, 1, 0);
			val2 = from_disk_byte[val];
			if(val2 < 0) {
				printf("Bad data area1, val:%02x,val2:%02x\n",
								val, val2);
				printf(" i:%03x,n_pos:%04x\n", i, dsk->nib_pos);
				break;
			}
			prev_val = val2 ^ prev_val;
			aux_buf[i] = prev_val;
		}

		/* rest of data area */
		for(i = 0; i < 0x100; i++) {
			val = iwm_read_data(dsk, 1, 0);
			val2 = from_disk_byte[val];
			if(val2 < 0) {
				printf("Bad data area2, read: %02x\n", val);
				printf("  nib_pos: %04x\n", dsk->nib_pos);
				break;
			}
			prev_val = val2 ^ prev_val;
			buf[i] = prev_val;
		}

		/* checksum */
		val = iwm_read_data(dsk, 1, 0);
		val2 = from_disk_byte[val];
		if(val2 < 0) {
			printf("Bad data area3, read: %02x\n", val);
			printf("  nib_pos: %04x\n", dsk->nib_pos);
			break;
		}
		if(val2 != prev_val) {
			printf("Bad data cksum, got %02x, wanted: %02x\n",
				val2, prev_val);
			printf("  nib_pos: %04x\n", dsk->nib_pos);
			break;
		}

		/* Got this far, data is good, merge aux_buf into buf */
		x = 0x55;
		for(i = 0; i < 0x100; i++) {
			val = aux_buf[x];
			val2 = (buf[i] << 1) + (val & 1);
			val = val >> 1;
			val2 = (val2 << 1) + (val & 1);
			buf[i] = val2;
			val = val >> 1;
			aux_buf[x] = val;
			x--;
			if(x < 0) {
				x = 0x55;
			}
		}
		sector_done[phys_sec] = 1;
		num_sectors_done++;
		if(num_sectors_done >= 16) {
			status = 0;
			break;
		}
	}

	tmp_nib_pos = dsk->nib_pos;
	iwm_move_to_track(dsk, save_qtr_track);
	dsk->nib_pos = save_nib_pos;
	g_fast_disk_unnib = 0;

	if(status == 0) {
		return 1;
	}

	printf("Nibblization not done, %02x sectors found on track %02x\n",
		num_sectors_done, qtr_track>>2);
	printf("my_nib_cnt: %04x, nib_pos: %04x, trk_len: %04x\n", my_nib_cnt,
		tmp_nib_pos, track_len);
	for(i = 0; i < 16; i++) {
		printf("sector_done[%d] = %d\n", i, sector_done[i]);
	}
	return -1;
}

int
iwm_denib_track35(Disk *dsk, Track *trk, int qtr_track, byte *outbuf)
{
	word32	buf_c00[0x100];
	word32	buf_d00[0x100];
	word32	buf_e00[0x100];
	byte	*buf;
	word32	tmp_5c, tmp_5d, tmp_5e;
	word32	tmp_66, tmp_67;
	int	sector_done[16];
	int	num_sectors_done;
	int	track_len;
	int	phys_track, phys_sec, phys_side, phys_capacity, cksum;
	int	tmp;
	int	track, side;
	int	num_sectors;
	int	val;
	int	val2;
	int	x, y;
	int	carry;
	int	my_nib_cnt;
	int	save_qtr_track;
	int	save_nib_pos;
	int	status;
	int	i;

	save_qtr_track = dsk->cur_qtr_track;
	save_nib_pos = dsk->nib_pos;

	iwm_move_to_track(dsk, qtr_track);

	dsk->nib_pos = 0;
	g_fast_disk_unnib = 1;

	track_len = trk->track_len;

	num_sectors = g_track_bytes_35[qtr_track >> 5] >> 9;

	for(i = 0; i < num_sectors; i++) {
		sector_done[i] = 0;
	}

	num_sectors_done = 0;

	val = 0;
	status = -1;
	my_nib_cnt = 0;

	track = qtr_track >> 1;
	side = qtr_track & 1;

	while(my_nib_cnt++ < 2*track_len) {
		/* look for start of a sector */
		if(val != 0xd5) {
			val = iwm_read_data(dsk, 1, 0);
			continue;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0xaa) {
			continue;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0x96) {
			continue;
		}

		/* It's a sector start */
		val = iwm_read_data(dsk, 1, 0);
		phys_track = from_disk_byte[val];
		if(phys_track != (track & 0x3f)) {
			printf("Track %02x.%d, read track %02x, %02x\n",
				track, side, phys_track, val);
			break;
		}

		phys_sec = from_disk_byte[iwm_read_data(dsk, 1, 0)];
		if(phys_sec < 0 || phys_sec >= num_sectors) {
			printf("Track %02x.%d, read sector %02x??\n",
				track, side, phys_sec);
			break;
		}
		phys_side = from_disk_byte[iwm_read_data(dsk, 1, 0)];

		if(phys_side != ((side << 5) + (track >> 6))) {
			printf("Track %02x.%d, read side %02x??\n",
				track, side, phys_side);
			break;
		}
		phys_capacity = from_disk_byte[iwm_read_data(dsk, 1, 0)];
		if(phys_capacity != 0x24 && phys_capacity != 0x22) {
			printf("Track %02x.%x capacity: %02x != 0x24/22\n",
				track, side, phys_capacity);
		}
		cksum = from_disk_byte[iwm_read_data(dsk, 1, 0)];

		tmp = phys_track ^ phys_sec ^ phys_side ^ phys_capacity;
		if(cksum != tmp) {
			printf("Track %02x.%d, sector %02x, cksum: %02x.%02x\n",
				track, side, phys_sec, cksum, tmp);
			break;
		}


		if(sector_done[phys_sec]) {
			printf("Already done sector %02x on track %02x.%x!\n",
				phys_sec, track, side);
			break;
		}

		/* So far so good, let's do it! */
		val = 0;
		for(i = 0; i < 38; i++) {
			val = iwm_read_data(dsk, 1, 0);
			if(val == 0xd5) {
				break;
			}
		}
		if(val != 0xd5) {
			printf("No data header, track %02x.%x, sec %02x\n",
				track, side, phys_sec);
			break;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0xaa) {
			printf("Bad data hdr1,val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			printf("nib_pos: %08x\n", dsk->nib_pos);
			break;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0xad) {
			printf("Bad data hdr2,val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			break;
		}

		buf = outbuf + (phys_sec << 9);

		/* check sector again */
		val = from_disk_byte[iwm_read_data(dsk, 1, 0)];
		if(val != phys_sec) {
			printf("Bad data hdr3,val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			break;
		}

		/* Data start! */
		tmp_5c = 0;
		tmp_5d = 0;
		tmp_5e = 0;
		y = 0xaf;
		carry = 0;

		while(y > 0) {
/* 626f */
			val = iwm_read_data(dsk, 1, 0);
			val2 = from_disk_byte[val];
			if(val2 < 0) {
				printf("Bad data area1b, read: %02x\n", val);
				printf(" i:%03x,n_pos:%04x\n", i, dsk->nib_pos);
				break;
			}
			tmp_66 = val2;

			tmp_5c = tmp_5c << 1;
			carry = (tmp_5c >> 8);
			tmp_5c = (tmp_5c + carry) & 0xff;

			val = iwm_read_data(dsk, 1, 0);
			val2 = from_disk_byte[val];
			if(val2 < 0) {
				printf("Bad data area2, read: %02x\n", val);
				break;
			}

			val2 = val2 + ((tmp_66 << 2) & 0xc0);

			val2 = val2 ^ tmp_5c;
			buf_c00[y] = val2;

			tmp_5e = val2 + tmp_5e + carry;
			carry = (tmp_5e >> 8);
			tmp_5e = tmp_5e & 0xff;
/* 62b8 */
			val = iwm_read_data(dsk, 1, 0);
			val2 = from_disk_byte[val];
			val2 = val2 + ((tmp_66 << 4) & 0xc0);
			val2 = val2 ^ tmp_5e;
			buf_d00[y] = val2;
			tmp_5d = val2 + tmp_5d + carry;

			carry = (tmp_5d >> 8);
			tmp_5d = tmp_5d & 0xff;

			y--;
			if(y <= 0) {
				break;
			}

/* 6274 */
			val = iwm_read_data(dsk, 1, 0);
			val2 = from_disk_byte[val];
			val2 = val2 + ((tmp_66 << 6) & 0xc0);
			val2 = val2 ^ tmp_5d;
			buf_e00[y+1] = val2;

			tmp_5c = val2 + tmp_5c + carry;
			carry = (tmp_5c >> 8);
			tmp_5c = tmp_5c & 0xff;
		}

/* 62d0 */
		val = iwm_read_data(dsk, 1, 0);
		val2 = from_disk_byte[val];

		tmp_66 = (val2 << 6) & 0xc0;
		tmp_67 = (val2 << 4) & 0xc0;
		val2 = (val2 << 2) & 0xc0;

		val = iwm_read_data(dsk, 1, 0);
		val2 = from_disk_byte[val] + val2;
		if(tmp_5e != (word32)val2) {
			printf("Checksum 5e bad: %02x vs %02x\n", tmp_5e, val2);
			printf("val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			break;
		}

		val = iwm_read_data(dsk, 1, 0);
		val2 = from_disk_byte[val] + tmp_67;
		if(tmp_5d != (word32)val2) {
			printf("Checksum 5d bad: %02x vs %02x\n", tmp_5e, val2);
			printf("val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			break;
		}

		val = iwm_read_data(dsk, 1, 0);
		val2 = from_disk_byte[val] + tmp_66;
		if(tmp_5c != (word32)val2) {
			printf("Checksum 5c bad: %02x vs %02x\n", tmp_5e, val2);
			printf("val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			break;
		}

		/* Whew, got it!...check for DE AA */
		val = iwm_read_data(dsk, 1, 0);
		if(val != 0xde) {
			printf("Bad data epi1,val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			printf("nib_pos: %08x\n", dsk->nib_pos);
			break;
		}

		val = iwm_read_data(dsk, 1, 0);
		if(val != 0xaa) {
			printf("Bad data epi2,val:%02x trk %02x.%x, sec %02x\n",
				val, track, side, phys_sec);
			break;
		}

		/* Now, convert buf_c/d/e to output */
/* 6459 */
		y = 0;
		for(x = 0xab; x >= 0; x--) {
			*buf++ = buf_c00[x];
			y++;
			if(y >= 0x200) {
				break;
			}

			*buf++ = buf_d00[x];
			y++;
			if(y >= 0x200) {
				break;
			}

			*buf++ = buf_e00[x];
			y++;
			if(y >= 0x200) {
				break;
			}
		}

		sector_done[phys_sec] = 1;
		num_sectors_done++;
		if(num_sectors_done >= num_sectors) {
			status = 0;
			break;
		}
		val = 0;
	}

	if(status < 0) {
		printf("dsk->nib_pos: %04x, status: %d\n", dsk->nib_pos,
			status);
		for(i = 0; i < num_sectors; i++) {
			printf("sector done[%d] = %d\n", i, sector_done[i]);
		}
	}

	iwm_move_to_track(dsk, save_qtr_track);
	dsk->nib_pos = save_nib_pos;
	g_fast_disk_unnib = 0;

	if(status == 0) {
		return 1;
	}

	printf("Nibblization not done, %02x sectors found on track %02x\n",
		num_sectors_done, qtr_track>>2);
	return -1;



}

/* ret = 1 -> dirty data written out */
/* ret = 0 -> not dirty, no error */
/* ret < 0 -> error */
int
disk_track_to_unix(Disk *dsk, int qtr_track, byte *outbuf)
{
	Track	*trk;
	int	disk_525;

	disk_525 = dsk->disk_525;

	trk = &(dsk->tracks[qtr_track]);

	if(trk->track_len == 0 || trk->track_dirty == 0) {
#if 0
		printf("disk_track_to_unix: dirty: %d\n", trk->track_dirty);
#endif
		return 0;
	}

	trk->track_dirty = 0;

	if((qtr_track & 3) && disk_525) {
		halt_printf("You wrote to phase %02x!  Can't wr bk to unix!\n",
			qtr_track);
		dsk->write_through_to_unix = 0;
		return -1;
	}

	if(disk_525) {
		return iwm_denib_track525(dsk, trk, qtr_track, outbuf);
	} else {
		return iwm_denib_track35(dsk, trk, qtr_track, outbuf);
	}
}


void
show_hex_data(byte *buf, int count)
{
	int	i;

	for(i = 0; i < count; i += 16) {
		printf("%04x: %02x %02x %02x %02x %02x %02x %02x %02x "
			"%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
			buf[i+0], buf[i+1], buf[i+2], buf[i+3], 
			buf[i+4], buf[i+5], buf[i+6], buf[i+7], 
			buf[i+8], buf[i+9], buf[i+10], buf[i+11], 
			buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
	}

}

void
disk_check_nibblization(Disk *dsk, int qtr_track, byte *buf, int size)
{
	byte	buffer[0x3000];
	Track	*trk;
	int	ret, ret2;
	int	i;

	if(size > 0x3000) {
		printf("size %08x is > 0x3000, disk_check_nibblization\n",size);
		exit(3);
	}

	for(i = 0; i < size; i++) {
		buffer[i] = 0;
	}

	trk = &(dsk->tracks[qtr_track]);

	if(dsk->disk_525) {
		ret = iwm_denib_track525(dsk, trk, qtr_track, &(buffer[0]));
	} else {
		ret = iwm_denib_track35(dsk, trk, qtr_track, &(buffer[0]));
	}

	ret2 = -1;
	for(i = 0; i < size; i++) {
		if(buffer[i] != buf[i]) {
			printf("buffer[%04x]: %02x != %02x\n", i, buffer[i],
				buf[i]);
			ret2 = i;
			break;
		}
	}

	if(ret != 1 || ret2 >= 0) {
		printf("disk_check_nib ret:%d, ret2:%d for q_track %03x\n",
			ret, ret2, qtr_track);
		show_hex_data(buf, 0x1000);
		show_hex_data(buffer, 0x1000);
		iwm_show_a_track(&(dsk->tracks[qtr_track]));

		exit(2);
	}
}


#define TRACK_BUF_LEN		0x2000

void
disk_unix_to_nib(Disk *dsk, int qtr_track, int unix_pos, int unix_len,
	int nib_len)
{
	byte	track_buf[TRACK_BUF_LEN];
	Track	*trk;
	int	must_clear_track;
	int	ret;
	int	len;
	int	i;
	
	/* Read track from dsk int track_buf */

	must_clear_track = 0;

	if(unix_len > TRACK_BUF_LEN) {
		printf("diks_unix_to_nib: requested len of image %s = %05x\n",
			dsk->name_ptr, unix_len);
	}

	if(unix_pos >= 0) {
		ret = lseek(dsk->fd, unix_pos, SEEK_SET);
		if(ret != unix_pos) {
			printf("lseek of disk %s len 0x%x ret: %d, errno: %d\n",
				dsk->name_ptr, unix_pos, ret, errno);
			must_clear_track = 1;
		}

		len = read(dsk->fd, track_buf, unix_len);
		if(len != unix_len) {
			printf("read of disk %s q_trk %d ret: %d, errno: %d\n",
				dsk->name_ptr, qtr_track, ret, errno);
			must_clear_track = 1;
		}
	}

	if(must_clear_track) {
		for(i = 0; i < TRACK_BUF_LEN; i++) {
			track_buf[i] = 0;
		}
	}

#if 0
	printf("Q_track %02x dumped out\n", qtr_track);

	for(i = 0; i < 4096; i += 32) {
		printf("%04x: %02x%02x%02x%02x%02x%02x%02x%02x "
			"%02x%02x%02x%02x%02x%02x%02x%02x "
			"%02x%02x%02x%02x%02x%02x%02x%02x "
			"%02x%02x%02x%02x%02x%02x%02x%02x\n", i,
			track_buf[i+0], track_buf[i+1], track_buf[i+2],
			track_buf[i+3], track_buf[i+4], track_buf[i+5],
			track_buf[i+6], track_buf[i+7], track_buf[i+8],
			track_buf[i+9], track_buf[i+10], track_buf[i+11],
			track_buf[i+12], track_buf[i+13], track_buf[i+14],
			track_buf[i+15], track_buf[i+16], track_buf[i+17],
			track_buf[i+18], track_buf[i+19], track_buf[i+20],
			track_buf[i+21], track_buf[i+22], track_buf[i+23],
			track_buf[i+24], track_buf[i+25], track_buf[i+26],
			track_buf[i+27], track_buf[i+28], track_buf[i+29],
			track_buf[i+30], track_buf[i+31]);
	}
#endif

	dsk->nib_pos = 0;		/* for consistency */

	trk = &(dsk->tracks[qtr_track]);
	trk->track_dirty = 0;
	trk->overflow_size = 0;
	trk->track_len = 2*nib_len;
	trk->unix_pos = unix_pos;
	trk->unix_len = unix_len;
	trk->dsk = dsk;
	trk->nib_area = (byte *)malloc(trk->track_len);

	/* create nibblized image */

	if(dsk->disk_525 && dsk->image_type == DSK_TYPE_NIB) {
		iwm_nibblize_track_nib525(dsk, trk, track_buf, qtr_track);
	} else if(dsk->disk_525) {
		iwm_nibblize_track_525(dsk, trk, track_buf, qtr_track);
	} else {
		iwm_nibblize_track_35(dsk, trk, track_buf, qtr_track);
	}
}

void
iwm_nibblize_track_nib525(Disk *dsk, Track *trk, byte *track_buf, int qtr_track)
{
	byte	*nib_ptr;
	byte	*trk_ptr;
	int	len;
	int	i;

	len = trk->track_len;
	trk_ptr = track_buf;
	nib_ptr = &(trk->nib_area[0]);
	for(i = 0; i < len; i += 2) {
		nib_ptr[i] = 8;
		nib_ptr[i+1] = *trk_ptr++;;
	}

	iwm_printf("Nibblized q_track %02x\n", qtr_track);
}

void
iwm_nibblize_track_525(Disk *dsk, Track *trk, byte *track_buf, int qtr_track)
{
	byte	partial_nib_buf[0x300];
	word32	*word_ptr;
	word32	val;
	word32	last_val;
	int	phys_sec;
	int	log_sec;
	int	num_sync;
	int	i;


	word_ptr = (word32 *)&(trk->nib_area[0]);
#ifdef KEGS_LITTLE_ENDIAN
	val = 0xff08ff08;
#else
	val = 0x08ff08ff;
#endif
	for(i = 0; i < trk->track_len; i += 4) {
		*word_ptr++ = val;
	}


	for(phys_sec = 0; phys_sec < 16; phys_sec++) {
		if(dsk->image_type == DSK_TYPE_DOS33) {
			log_sec = phys_to_dos_sec[phys_sec];
		} else {
			log_sec = phys_to_prodos_sec[phys_sec];
		}

		/* Create sync headers */
		if(phys_sec == 0) {
			num_sync = 70;
		} else {
			num_sync = 14;
		}

		for(i = 0; i < num_sync; i++) {
			disk_nib_out(dsk, 0xff, 10);
		}
		disk_nib_out(dsk, 0xd5, 10);		/* prolog */
		disk_nib_out(dsk, 0xaa, 8);		/* prolog */
		disk_nib_out(dsk, 0x96, 8);		/* prolog */
		disk_4x4_nib_out(dsk, dsk->vol_num);
		disk_4x4_nib_out(dsk, qtr_track >> 2);
		disk_4x4_nib_out(dsk, phys_sec);
		disk_4x4_nib_out(dsk, dsk->vol_num ^ (qtr_track>>2) ^ phys_sec);
		disk_nib_out(dsk, 0xde, 8);		/* epi */
		disk_nib_out(dsk, 0xaa, 8);		/* epi */
		disk_nib_out(dsk, 0xeb, 8);		/* epi */

		/* Inter sync */
		disk_nib_out(dsk, 0xff, 8);
		for(i = 0; i < 5; i++) {
			disk_nib_out(dsk, 0xff, 10);
		}
		disk_nib_out(dsk, 0xd5, 10);	/* data prolog */
		disk_nib_out(dsk, 0xaa, 8);	/* data prolog */
		disk_nib_out(dsk, 0xad, 8);	/* data prolog */

		sector_to_partial_nib( &(track_buf[log_sec*256]),
			&(partial_nib_buf[0]));

		last_val = 0;
		for(i = 0; i < 0x156; i++) {
			val = partial_nib_buf[i];
			disk_nib_out(dsk, to_disk_byte[last_val ^ val], 8);
			last_val = val;
		}
		disk_nib_out(dsk, to_disk_byte[last_val], 8);

		/* data epilog */
		disk_nib_out(dsk, 0xde, 8);	/* epi */
		disk_nib_out(dsk, 0xaa, 8);	/* epi */
		disk_nib_out(dsk, 0xeb, 8);	/* epi */
		disk_nib_out(dsk, 0xff, 8);
		for(i = 0; i < 6; i++) {
			disk_nib_out(dsk, 0xff, 10);
		}
	}

	/* finish nibblization */
	disk_nib_end_track(dsk);

	iwm_printf("Nibblized q_track %02x\n", qtr_track);

	if(g_check_nibblization) {
		disk_check_nibblization(dsk, qtr_track, &(track_buf[0]),0x1000);
	}
}

void
iwm_nibblize_track_35(Disk *dsk, Track *trk, byte *track_buf, int qtr_track)
{
	int	phys_to_log_sec[16];
	word32	buf_c00[0x100];
	word32	buf_d00[0x100];
	word32	buf_e00[0x100];
	byte	*buf;
	word32	*word_ptr;
	word32	val;
	int	num_sectors;
	int	unix_len;
	int	log_sec;
	int	phys_sec;
	int	track;
	int	side;
	int	interleave;
	int	num_sync;
	word32	phys_track, phys_side, capacity, cksum;
	word32	tmp_5c, tmp_5d, tmp_5e, tmp_5f;
	word32	tmp_63, tmp_64, tmp_65;
	word32	acc_hi;
	int	carry;
	int	x, y;
	int	i;

	word_ptr = (word32 *)&(trk->nib_area[0]);
#ifdef KEGS_LITTLE_ENDIAN
	val = 0xff08ff08;
#else
	val = 0x08ff08ff;
#endif
	if(trk->track_len & 3) {
		halt_printf("track_len: %08x is not a multiple of 4\n",
				trk->track_len);
	}

	for(i = 0; i < trk->track_len; i += 4) {
		*word_ptr++ = val;
	}

	unix_len = trk->unix_len;

	num_sectors = (unix_len >> 9);

	for(i = 0; i < num_sectors; i++) {
		phys_to_log_sec[i] = -1;
	}

	phys_sec = 0;
	interleave = 2;
	for(log_sec = 0; log_sec < num_sectors; log_sec++) {
		while(phys_to_log_sec[phys_sec] >= 0) {
			phys_sec++;
			if(phys_sec >= num_sectors) {
				phys_sec = 0;
			}
		}
		phys_to_log_sec[phys_sec] = log_sec;
		phys_sec += interleave;
		if(phys_sec >= num_sectors) {
			phys_sec -= num_sectors;
		}
	}

	track = qtr_track >> 1;
	side = qtr_track & 1;
	for(phys_sec = 0; phys_sec < num_sectors; phys_sec++) {

		log_sec = phys_to_log_sec[phys_sec];
		if(log_sec < 0) {
			printf("Track: %02x.%x phys_sec: %02x = %d!\n",
				track, side, phys_sec, log_sec);
			exit(2);
		}

		/* Create sync headers */
		if(phys_sec == 0) {
			num_sync = 400;
		} else {
			num_sync = 54;
		}

		for(i = 0; i < num_sync; i++) {
			disk_nib_out(dsk, 0xff, 10);
		}

		disk_nib_out(dsk, 0xd5, 10);		/* prolog */
		disk_nib_out(dsk, 0xaa, 8);		/* prolog */
		disk_nib_out(dsk, 0x96, 8);		/* prolog */

		phys_track = track & 0x3f;
		phys_side = (side << 5) + (track >> 6);
		capacity = 0x22;
		disk_nib_out(dsk, to_disk_byte[phys_track], 8);	/* trk */
		disk_nib_out(dsk, to_disk_byte[log_sec], 8);	/* sec */
		disk_nib_out(dsk, to_disk_byte[phys_side], 8);	/* sides+trk */
		disk_nib_out(dsk, to_disk_byte[capacity], 8);	/* capacity*/

		cksum = (phys_track ^ log_sec ^ phys_side ^ capacity) & 0x3f;
		disk_nib_out(dsk, to_disk_byte[cksum], 8);	/* cksum*/

		disk_nib_out(dsk, 0xde, 8);		/* epi */
		disk_nib_out(dsk, 0xaa, 8);		/* epi */

		/* Inter sync */
		for(i = 0; i < 5; i++) {
			disk_nib_out(dsk, 0xff, 10);
		}
		disk_nib_out(dsk, 0xd5, 10);	/* data prolog */
		disk_nib_out(dsk, 0xaa, 8);	/* data prolog */
		disk_nib_out(dsk, 0xad, 8);	/* data prolog */
		disk_nib_out(dsk, to_disk_byte[log_sec], 8);	/* sec again */

		/* do nibblizing! */
		buf = track_buf + (log_sec << 9);

/* 6320 */
		tmp_5e = 0;
		tmp_5d = 0;
		tmp_5c = 0;
		y = 0;
		x = 0xaf;
		buf_c00[0] = 0;
		buf_d00[0] = 0;
		buf_e00[0] = 0;
		buf_e00[1] = 0;
		for(y = 0x4; y > 0; y--) {
			buf_c00[x] = 0;
			buf_d00[x] = 0;
			buf_e00[x] = 0;
			x--;
		}

		while(x >= 0) {
/* 6338 */
			tmp_5c = tmp_5c << 1;
			carry = (tmp_5c >> 8);
			tmp_5c = (tmp_5c + carry) & 0xff;

			val = buf[y];
			tmp_5e = val + tmp_5e + carry;
			carry = (tmp_5e >> 8);
			tmp_5e = tmp_5e & 0xff;

			val = val ^ tmp_5c;
			buf_c00[x] = val;
			y++;
/* 634c */
			val = buf[y];
			tmp_5d = tmp_5d + val + carry;
			carry = (tmp_5d >> 8);
			tmp_5d = tmp_5d & 0xff;
			val = val ^ tmp_5e;
			buf_d00[x] = val;
			y++;
			x--;
			if(x <= 0) {
				break;
			}

/* 632a */
			val = buf[y];
			tmp_5c = tmp_5c + val + carry;
			carry = (tmp_5c >> 8);
			tmp_5c = tmp_5c & 0xff;

			val = val ^ tmp_5d;
			buf_e00[x+1] = val;
			y++;
		}

/* 635f */
		val = ((tmp_5c >> 2) ^ tmp_5d) & 0x3f;
/* 6367 */
		val = (val ^ tmp_5d) >> 2;
/* 636b */
		val = (val ^ tmp_5e) & 0x3f;
/* 636f */
		val = (val ^ tmp_5e) >> 2;
/* 6373 */
		tmp_5f = val;
/* 6375 */
		tmp_63 = 0;
		tmp_64 = 0;
		tmp_65 = 0;
		acc_hi = 0;


		y = 0xae;
		while(y >= 0) {
/* 63e4 */
			/* write out acc_hi */
			val = to_disk_byte[acc_hi & 0x3f];
			disk_nib_out(dsk, val, 8);

/* 63f2 */
			val = to_disk_byte[tmp_63 & 0x3f];
			tmp_63 = buf_c00[y];
			acc_hi = tmp_63 >> 6;
			disk_nib_out(dsk, val, 8);
/* 640b */
			val = to_disk_byte[tmp_64 & 0x3f];
			tmp_64 = buf_d00[y];
			acc_hi = (acc_hi << 2) + (tmp_64 >> 6);
			disk_nib_out(dsk, val, 8);
			y--;
			if(y < 0) {
				break;
			}

/* 63cb */
			val = to_disk_byte[tmp_65 & 0x3f];
			tmp_65 = buf_e00[y+1];
			acc_hi = (acc_hi << 2) + (tmp_65 >> 6);
			disk_nib_out(dsk, val, 8);
		}
/* 6429 */
		val = to_disk_byte[tmp_5f & 0x3f];
		disk_nib_out(dsk, val, 8);

		val = to_disk_byte[tmp_5e & 0x3f];
		disk_nib_out(dsk, val, 8);

		val = to_disk_byte[tmp_5d & 0x3f];
		disk_nib_out(dsk, val, 8);

		val = to_disk_byte[tmp_5c & 0x3f];
		disk_nib_out(dsk, val, 8);

/* 6440 */
		/* data epilog */
		disk_nib_out(dsk, 0xde, 8);	/* epi */
		disk_nib_out(dsk, 0xaa, 8);	/* epi */
		disk_nib_out(dsk, 0xff, 8);
	}


	disk_nib_end_track(dsk);

	if(g_check_nibblization) {
		disk_check_nibblization(dsk, qtr_track, &(track_buf[0]),
			unix_len);
	}
}

void
disk_4x4_nib_out(Disk *dsk, word32 val)
{
	disk_nib_out(dsk, 0xaa | (val >> 1), 8);
	disk_nib_out(dsk, 0xaa | val, 8);
}

void
disk_nib_out(Disk *dsk, byte val, int size)
{
	Track	*trk;
	int	pos;
	int	old_size;
	int	track_len;
	int	overflow_size;
	int	qtr_track;


	qtr_track = dsk->cur_qtr_track;

	trk = &(dsk->tracks[qtr_track]);

	track_len = trk->track_len;

	if(track_len <= 10) {
		printf("Writing to an invalid qtr track: %02x!\n", qtr_track);
		printf("name: %s, track_len: %08x, val: %08x, size: %d\n",
			dsk->name_ptr, track_len, val, size);
		exit(1);
		return;
	}

	trk->track_dirty = 1;
	dsk->disk_dirty = 1;

	pos = trk->dsk->nib_pos;
	overflow_size = trk->overflow_size;
	if(pos >= track_len) {
		pos = 0;
	}

	old_size = trk->nib_area[pos];


	while(size >= (10 + old_size)) {
		size = size - old_size;
		pos += 2;
		if(pos >= track_len) {
			pos = 0;
		}
		old_size = trk->nib_area[pos];
	}

	if(size > 10) {
		size = 10;
	}

	if((val & 0x80) == 0) {
		val |= 0x80;
	}

	trk->nib_area[pos++] = size;
	trk->nib_area[pos++] = val;
	if(pos >= track_len) {
		pos = 0;
	}

	overflow_size += (size - old_size);
	if((overflow_size > 8) && (size > 8)) {
		overflow_size -= trk->nib_area[pos];
		trk->nib_area[pos++] = 0;
		trk->nib_area[pos++] = 0;
		if(pos >= track_len) {
			pos = 0;
		}
	} else if(overflow_size < -64) {
		halt_printf("overflow_sz:%03x, pos:%02x\n",overflow_size,pos);
	}

	trk->dsk->nib_pos = pos;
	trk->overflow_size = overflow_size;

	if((val & 0x80) == 0 || size < 8) {
		halt_printf("disk_nib_out, wrote %02x, size: %d\n", val, size);
	}
}

void
disk_nib_end_track(Disk *dsk)
{
	int	qtr_track;

	dsk->nib_pos = 0;
	qtr_track = dsk->cur_qtr_track;
	dsk->tracks[qtr_track].track_dirty = 0;

	dsk->disk_dirty = 0;
}

void
iwm_show_track(int slot_drive, int track)
{
	Disk	*dsk;
	Track	*trk;
	int	drive;
	int	sel35;
	int	qtr_track;

	if(slot_drive < 0) {
		drive = iwm.drive_select;
		sel35 = g_apple35_sel;
	} else {
		drive = slot_drive & 1;
		sel35 = !((slot_drive >> 1) & 1);
	}
	
	if(sel35) {
		dsk = &(iwm.drive35[drive]);
	} else {
		dsk = &(iwm.drive525[drive]);
	}

	if(track < 0) {
		qtr_track = dsk->cur_qtr_track;
	} else {
		qtr_track = track;
	}
	trk = &(dsk->tracks[qtr_track]);

	if(trk->track_len <= 0) {
		printf("Track_len: %d\n", trk->track_len);
		printf("No track for type: %d, drive: %d, qtrk: %02x\n",
			g_apple35_sel, drive, qtr_track);
		return;
	}

	printf("Current drive: %d, q_track: %02x\n", drive, qtr_track);

	iwm_show_a_track(trk);
}

void
iwm_show_a_track(Track *trk)
{
	int	sum;
	int	len;
	int	pos;
	int	i;

	printf("  Showtrack:dirty: %d, pos: %04x, ovfl: %04x, len: %04x\n",
		trk->track_dirty, trk->dsk->nib_pos,
		trk->overflow_size, trk->track_len);

	len = trk->track_len;
	printf("Track len in bytes: %04x\n", len);
	if(len >= 2*15000) {
		len = 2*15000;
		printf("len too big, using %04x\n", len);
	}

	pos = 0;
	for(i = 0; i < len; i += 16) {
		printf("%04x: %2d,%02x %2d,%02x %2d,%02x %2d,%02x "
			"%2d,%02x %2d,%02x %2d,%02x %2d,%02x\n", pos,
			trk->nib_area[pos], trk->nib_area[pos+1],
			trk->nib_area[pos+2], trk->nib_area[pos+3],
			trk->nib_area[pos+4], trk->nib_area[pos+5],
			trk->nib_area[pos+6], trk->nib_area[pos+7],
			trk->nib_area[pos+8], trk->nib_area[pos+9],
			trk->nib_area[pos+10], trk->nib_area[pos+11],
			trk->nib_area[pos+12], trk->nib_area[pos+13],
			trk->nib_area[pos+14], trk->nib_area[pos+15]);
		pos += 16;
		if(pos >= len) {
			pos -= len;
		}
	}

	sum = 0;
	for(i = 0; i < len; i += 2) {
		sum += trk->nib_area[i];
	}

	printf("bit_sum: %d, expected: %d, overflow_size: %d\n",
		sum, len*8/2, trk->overflow_size);
}

